package org.micode.common.base;

import lombok.extern.slf4j.Slf4j;
import org.micode.common.config.ApplicationConfig;
import org.micode.common.domain.ApiResult;
import org.micode.common.domain.base.IUserCache;
import org.micode.common.enums.ResultEnum;

import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 控制类基类
 */
@Slf4j
public abstract class BaseController<WT extends Object>
        extends AbstractController {

    protected abstract <K> void copyLoginUserFields(IUserCache user, K example);

    protected abstract <K> void fillUpdFields(K dest, Long userId, String loginApp, String loginIp);

    protected abstract String getToken(WT webObject);

    protected abstract String getLoginClientApp(WT webObject);

    protected abstract String getLoginClientIp(WT webObject);

    protected abstract IUserCache currentLoginUser(WT webObject);

    protected <K> void fillLoginFields(WT webObject, K theObj) {
        IUserCache user = currentLoginUser(webObject);
        if (null != user) {
            copyLoginUserFields(user, theObj);
            fillUpdFields(theObj, user.getUserId(), user.getLoginApp(), user.getLoginIp());
        }
    }

    protected Long currentUserId(WT webObject) {
        Long ret = currentUserIdFromCache(webObject);
        if (null == ret) {
            ret = currentUserIdFromHeader(webObject);
        }
        return ret;
    }

    private Long currentUserIdFromCache(WT webObject) {
        IUserCache user = this.getCurrentUserCache(webObject);
        if (null == user) {
            return null;
        } else {
            return user.getUserId();
        }
    }

    protected abstract IUserCache getCurrentUserCache(WT webObject);

    protected abstract Long currentUserIdFromHeader(WT webObject);

    protected <OUT_T> ApiResult<OUT_T> doInvokeMethod(Supplier<OUT_T> function,
                                                      ResultEnum nullResult,
                                                      String exceptionMsg) {
        try {
            OUT_T ret = function.get();
            if (null != ret) {
                return successResultWithData(ret);
            } else {
                return nullResult == ResultEnum.SUCCESS
                        ? successResultWithData(null)
                        : failResult(nullResult);
            }
        } catch (Exception e) {
            return doInvokeFail(exceptionMsg, e);
        }
    }

    protected <IN_T, OUT_T> ApiResult<OUT_T> doInvokeMethod(Function<IN_T, OUT_T> function,
                                                            IN_T inputArg,
                                                            ResultEnum nullResult,
                                                            String exceptionMsg) {
//        try {
            OUT_T ret = function.apply(inputArg);
            if (null != ret) {
                return successResultWithData(ret);
            } else {
                return nullResult == ResultEnum.SUCCESS
                        ? successResultWithData(null)
                        : failResult(nullResult);
            }
//        } catch (Exception e) {
//            return doInvokeFail(exceptionMsg, e);
//        }
    }

    protected <T> ApiResult<T> doInvokeFail(String argsMsg, Exception e) {
        log.error("Invoke fail: {}",
                null == argsMsg ? "" : argsMsg + ", ",
                e);
        String msg = null != argsMsg ? argsMsg : e.getMessage();
        return failResult(ResultEnum.INVOKE_FAIL.getCode(), "Invoke fail: " + msg);
    }

    protected <T> ApiResult<T> doReturnFail(Integer resultCode, String errorMsg) {
        return doReturn(failResult(resultCode, errorMsg));
    }

    protected <T> ApiResult<T> doReturnFail(String errorMsg) {
        return doReturn(failResult(ResultEnum.INVOKE_FAIL.getCode(), errorMsg));
    }

    protected <T> ApiResult<T> doReturnFailToken() {
        return doReturn(failResult(ResultEnum.TOKEN_INVALID.getCode(), ResultEnum.TOKEN_INVALID.getName()));
    }

    protected <T> ApiResult<T> doReturnSuccess(T ret) {
        return doReturn(successResultWithData(ret));
    }

    private <T> T doReturn(T ret) {
        if (log.isDebugEnabled()) {
            log.debug("The result: {}", ApplicationConfig.objectToString(ret));
        }
        return ret;
    }

}
